import pygame
import sys
import random
from datetime import datetime
import threading
from typing import List, Dict, Any, Tuple, Optional

# 常量定义
SCREEN_WIDTH, SCREEN_HEIGHT = 950, 700
BLOCK_SIZE = 30
MOVE_FREQUENCY = 20  # 每 10 帧移动一次
start_x_block = 150
start_y_block = 150

# 颜色常量
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (128, 128, 128)
BLUE = (0, 0, 255)
RED = (255, 0, 0)
YELLOW = (255, 255, 0)
GOLD = (255, 215, 0)  # 金色文字
MINIMAP_BG = (50, 50, 50)  # 小地图背景
MINIMAP_BORDER = (200, 200, 200)  # 小地图边框

# 类型别名
Block = Dict[str, Any]
Player = Dict[str, Any]
Button = Dict[str, Any]

# 初始化pygame
pygame.init()
pygame.font.init()  # 显式初始化字体模块
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("Pygame 自动寻路")
clock = pygame.time.Clock()
print_lock = threading.Lock()

# 全局变量
blocks: List[Block] = []
players: List[Player] = []
no_way_blocks: List[Block] = []
buttons: List[Block] = []
move_counter = 0
end_block = 0
start_game_flag = "None"
# 图片资源全局变量
images: Dict[str, Optional[pygame.Surface]] = {}

# 新增常量定义（界面信息相关）
MINIMAP_SIZE = 150  # 小地图尺寸
MINIMAP_POS = (SCREEN_WIDTH - MINIMAP_SIZE - 5, 5)  # 小地图位置（右上角）
COORD_BOX_SIZE = (120, 40)  # 坐标显示框大小

# 按钮样式常量
DARK_BORDER = (40, 40, 40)  # 边框深色
LIGHT_BORDER = (200, 200, 200)  # 悬停闪烁色
BUTTON_COLORS = {
    'start': (70, 130, 180),   # 钢蓝色
    'stop': (220, 50, 50),     # 珊瑚红
    'reset': (60, 179, 113)    # 浅海绿
}  # 固定功能色替代随机色

# 加载像素字体
try:
    pixel_font = pygame.font.Font("04B_03.ttf", 16)
except:
    pixel_font = pygame.font.SysFont(["Courier New", "monospace"], 16, bold=True)  # 备选字体

def random_color() -> Tuple[int, int, int]:
    """生成随机颜色"""
    return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))

def load_image() -> None:
    """加载游戏图片资源"""
    global  images
    imageAll = {
        "wall": "./img/wall.png",
        "road": "./img/road.png",
        "men": "./img/men.png",
        "player": "./img/player.png",
    }
    # 遍历imageAll
    for key, value in imageAll.items():
        # 加载图片
        try:
            wall_image = pygame.image.load(value)
            wall_image = pygame.transform.scale(wall_image, (BLOCK_SIZE, BLOCK_SIZE))
            images[key] = wall_image
        except Exception as e:
            print(f"加载{key}图片时发生错误: {e}")
            images[key] = None

def create_migong() -> None:
    """创建迷宫"""
    try:
        global images
        for x in range(20):
            b_x = start_x_block + x * BLOCK_SIZE
            for y in range(15):
                b_y = start_y_block + y * BLOCK_SIZE
                vx = random.randint(-5, 5)
                vy = random.randint(-5, 5)
                colour = BLACK
                way = False

                if (x % 5 == 0 or y % 5 == 0) and (x % 10 != 0 or y % 10 != 0):
                    colour = WHITE
                    way = True

                new_block = {
                    'x': b_x,
                    'y': b_y,
                    'vx': vx,
                    'vy': vy,
                    'colour': colour,
                    'way': way,
                    'role': 'block',
                    'ending': False,
                    'width': BLOCK_SIZE,
                    'height': BLOCK_SIZE,
                    'image': images['road'] if way else images['wall'], # 添加图片
                    'grid_x': x,  # 新增网格坐标
                    'grid_y': y   # 新增网格坐标
                }
                blocks.append(new_block)

        # 设置起点和终点
        blocks[0]['way'] = True
        blocks[0]['colour'] = RED
        blocks[0]['image'] = images['men']
        blocks[15*0+11]['way'] = False
        blocks[15*0+11]['colour'] = GRAY
        blocks[15*1+5]['way'] = False
        blocks[15*1+5]['colour'] = GRAY
        blocks[15*1+10]['way'] = False
        blocks[15*1+10]['colour'] = GRAY
        blocks[15*5+6]['way'] = False
        blocks[15*5+6]['colour'] = GRAY
        blocks[15*9+10]['way'] = False
        blocks[15*9+10]['colour'] = GRAY
        blocks[15*10+1]['way'] = False
        blocks[15*10+1]['colour'] = GRAY
        blocks[15*11+10]['way'] = False
        blocks[15*11+10]['colour'] = GRAY
        blocks[15*15+4]['way'] = False
        blocks[15*15+4]['colour'] = GRAY
        blocks[15*15+14]['way'] = True
        blocks[15*15+14]['ending'] = True
        blocks[15*15+14]['colour'] = RED
        blocks[15*15+14]['image'] = images['men']

        for block in blocks:
            block['image'] = images['road'] if block['way'] else images['wall']
        blocks[0]['image'] = images['men']
        blocks[15*15+14]['image'] = images['men']

    except Exception as e:
        print(f"创建迷宫时发生错误: {e}")

def create_button() -> None:
    """创建按钮"""
    button_defs = [
        {'x': start_x_block, 'y': start_y_block-50, 'width': 80, 'height': 30, 'text': 'start', 'value': 'start'},
        {'x': start_x_block+100, 'y': start_y_block-50, 'width': 80, 'height': 30, 'text': 'stop', 'value': 'stop'},
        {'x': start_x_block+200, 'y': start_y_block-50, 'width': 80, 'height': 30, 'text': 'reset', 'value': 'reset'}
    ]
    for btn in button_defs:
        # 创建带描边的文字
        text_surf = pixel_font.render(btn['text'], True, (255, 255, 255))  # 白色文字
        text_shadow = pixel_font.render(btn['text'], True, (0, 0, 0))      # 黑色描边

        button = {
            **btn,
            'colour': BUTTON_COLORS[btn['value']],
            'role': 'button',
            'text': text_surf,
            'text_shadow': text_shadow,
            'hover': False,  # 悬停状态
            'pressed': False,  # 按压状态
            'rect': pygame.Rect(btn['x'], btn['y'], btn['width'], btn['height'])  # 碰撞检测矩形
        }
        buttons.append(button)

def create_player() -> None:
    """创建玩家角色"""
    try:
        player_block_2 = {
            'x': blocks[0]['x'],
            'y': blocks[0]['y'],
            'vx': blocks[0]['x'],
            'vy': blocks[0]['y'],
            'colour': YELLOW,
            'role': 'player',
            'step_counter': 0,
            'name': 'palyer2',
            'image': images['player'],
            'grid_x': 0,  # 新增网格坐标
            'grid_y': 0   # 新增网格坐标
        }
        players.append(player_block_2)

    except Exception as e:
        print(f"创建玩家时发生错误: {e}")


def move_player_block(player_block: Player) -> None:
    """基础移动逻辑"""
    try:
        # 获取当前玩家位置
        current_x, current_y = player_block['x'], player_block['y']

        # 获取所有可移动的方向
        directions = []
        for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
            new_x = current_x + BLOCK_SIZE * dx
            new_y = current_y + BLOCK_SIZE * dy

            # 检查新位置是否在边界内且可通行
            if (new_x >= blocks[0]['x'] and new_x <= blocks[-1]['x'] and
                    new_y >= blocks[0]['y'] and new_y <= blocks[-1]['y']):
                for block in blocks:
                    if block['x'] == new_x and block['y'] == new_y and block['way']:
                        directions.append((dx, dy))
                        break

        # 如果有可移动的方向，则随机选择一个方向移动
        if directions:
            dx, dy = random.choice(directions)
            player_block['x'] = current_x + BLOCK_SIZE * dx
            player_block['y'] = current_y + BLOCK_SIZE * dy
            # 更新网格坐标
            player_block['grid_x'] += dx
            player_block['grid_y'] += dy
    except Exception as e:
        print(f"移动玩家方块时发生错误: {e}")


def move_player_concurrently(player_block: Player) -> None:
    """并发移动处理"""
    try:
        # 备份原始位置
        original_x, original_y = player_block['x'], player_block['y']
        original_grid_x, original_grid_y = player_block['grid_x'], player_block['grid_y']

        move_player_block(player_block)
        move_count_try = 0
        while True:
            move_count_try += 1
            player_block['step_counter'] += 1
            current_pos = (player_block['x'], player_block['y'])
            no_way_positions = [(block['x'], block['y']) for block in no_way_blocks]

            if current_pos in no_way_positions:
                # 还原位置并重新移动
                player_block['x'], player_block['y'] = original_x, original_y
                player_block['grid_x'], player_block['grid_y'] = original_grid_x, original_grid_y
                move_player_block(player_block)
                if move_count_try > 10:
                    # 尝试次数过多，停止移动
                    break
            else:
                # 打印移动信息
                current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                # print(f"玩家：{player_block['name']} 当前时间：{current_time}   步数：{player_block['step_counter']}")
                break

    except Exception as e:
        print(f"并发移动玩家时发生错误: {e}")


def check_block_surroundings(block: Block) -> None:
    """检查方块周围的路径情况"""
    global images
    try:
        directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
        accessible_count = 0

        for dx, dy in directions:
            x = block['x'] + dx * BLOCK_SIZE
            y = block['y'] + dy * BLOCK_SIZE

            # 检查边界
            if (x < blocks[0]['x'] or x > blocks[-1]['x'] or
                    y < blocks[0]['y'] or y > blocks[-1]['y']):
                continue

            # 检查该位置是否有不可通过的方块
            is_blocked = False
            for b in blocks:
                if b['x'] == x and b['y'] == y and not b['way']:
                    is_blocked = True
                    break

            if not is_blocked:
                accessible_count += 1

        # 如果周围可通行的位置少于2个，则标记为不可通过
        if accessible_count < 2:
            block['way'] = False
            block['colour'] = GRAY
            block['image'] = images['road'] if block['way'] else images['wall']

    except Exception as e:
        print(f"检查方块周围路径时发生错误: {e}")

def draw_button(button: Button)-> None:
    """绘制按钮（优化版：双层边框+悬停效果）"""
    x, y, w, h = button['x'], button['y'], button['width'], button['height']

    # 处理按压状态
    if button['pressed']:
        # 颜色加深算法
        pressed_color = tuple(max(0, c-30) for c in button['colour'])
        pygame.draw.rect(screen, DARK_BORDER, (x, y, w, h))  # 边框不变
        pygame.draw.rect(screen, pressed_color, (x+1, y+1, w-2, h-2))  # 主体内缩
        text_offset = (1, 1)  # 文字下移右移1px模拟按压
    elif button['hover']:
        # 悬停状态：外层闪烁边框+轻微放大
        border_color = LIGHT_BORDER if pygame.time.get_ticks() % 600 < 300 else DARK_BORDER
        pygame.draw.rect(screen, border_color, (x-2, y-2, w+4, h+4))  # 外层边框
        pygame.draw.rect(screen, button['colour'], (x-1, y-1, w+2, h+2))  # 主体放大
        text_offset = (0, 0)
    else:
        # 正常状态：双层边框
        pygame.draw.rect(screen, DARK_BORDER, (x-1, y-1, w+2, h+2))  # 外层边框
        pygame.draw.rect(screen, button['colour'], (x, y, w, h))        # 主体
        text_offset = (0, 0)

    # 绘制文字描边（先画阴影再画主体）
    text_x = x + (w - button['text'].get_width()) // 2  # 水平居中
    text_y = y + (h - button['text'].get_height()) // 2  # 垂直居中
    screen.blit(button['text_shadow'], (text_x+1, text_y+1))  # 阴影偏移
    screen.blit(button['text'], (text_x + text_offset[0], text_y + text_offset[1]))

def draw_block(block: Block) -> None:
    """绘制单个方块"""
    try:
        if block['image'] is not None:
            # 绘制图片
            screen.blit(block['image'], (block['x'], block['y']))
        else:
            pygame.draw.rect(screen, block['colour'], (block['x'], block['y'], BLOCK_SIZE, BLOCK_SIZE))
        # 绘制边框
        pygame.draw.rect(screen, BLACK, (block['x'], block['y'], BLOCK_SIZE, BLOCK_SIZE), 1)

    except Exception as e:
        print(f"绘制方块时发生错误: {e}")


def draw_player(player: Player) -> None:
    """绘制玩家角色"""
    try:
        if player['image'] is not None:
            screen.blit(player['image'], (player['x'], player['y']))
        else:
            pygame.draw.rect(screen, player['colour'], (player['x'], player['y'], BLOCK_SIZE, BLOCK_SIZE))
    except Exception as e:
        print(f"绘制玩家时发生错误: {e}")


def check_collision(player: Player) -> Tuple[bool, bool]:
    """碰撞检测"""
    try:
        player_rect = pygame.Rect(player['x'], player['y'], BLOCK_SIZE, BLOCK_SIZE)
        is_move_valid = True
        game_over = False

        for block in blocks:
            block_rect = pygame.Rect(block['x'], block['y'], BLOCK_SIZE, BLOCK_SIZE)

            if player_rect.colliderect(block_rect):
                if not block['way']:
                    is_move_valid = False
                    if block not in no_way_blocks:
                        no_way_blocks.append(block)
                    break

                elif block['way']:
                    if not block['ending']:
                        check_block_surroundings(block)
                        if not block['way']:
                            if block not in no_way_blocks:
                                no_way_blocks.append(block)
                        break
                    else:
                        print('游戏结束！')
                        game_over = True
                        break

        return is_move_valid, game_over

    except Exception as e:
        print(f"碰撞检测时发生错误: {e}")
        return False, False


def handle_events() -> bool:
    """处理事件循环"""
    global start_game_flag,images
    try:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
            # 鼠标移动检测（更新悬停状态）
            elif event.type == pygame.MOUSEMOTION:
                for button in buttons:
                    button['hover'] = button['rect'].collidepoint(event.pos)
            # 鼠标点击检测
            elif event.type == pygame.MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                for button in buttons:
                    button_rect = pygame.Rect(button['x'], button['y'], button['width'], button['height'])
                    if button_rect.collidepoint(mouse_pos):
                        start_game_flag = button['value']
                        button['pressed'] = True
                        print(f"点击了按钮：{start_game_flag}")

                for block in blocks :
                    block_rect = pygame.Rect(block['x'], block['y'], block['width'], block['height'])
                    if block_rect.collidepoint(mouse_pos):
                        print(f"点击了迷宫块：({block['grid_x']},{block['grid_y']})")
                        block['way'] = not block['way']
                        block['colour'] = WHITE if block['way'] else GRAY
                        block['image'] = images['road'] if block['way'] else images['wall']
            # 鼠标释放检测
            elif event.type == pygame.MOUSEBUTTONUP:
                if event.button == 1:
                    for button in buttons:
                        button['pressed'] = False

        return True
    except Exception as e:
        print(f"处理事件时发生错误: {e}")
        return False


def update_game_state() -> bool:
    """更新游戏状态"""
    try:
        global move_counter

        move_counter += 1
        if move_counter >= MOVE_FREQUENCY:
            # 移动玩家
            for player in players:
                move_player_concurrently(player)

            # 碰撞检测
            for player in players:
                is_move_valid, game_over = check_collision(player)

                if game_over:
                    return False

            # 重置移动计数器
            move_counter = 0

        return True

    except Exception as e:
        print(f"更新游戏状态时发生错误: {e}")
        return False


def draw_coordinates() -> None:
    """绘制鼠标坐标信息"""
    if not blocks:  # 确保迷宫已创建
        return

    mouse_x, mouse_y = pygame.mouse.get_pos()
    # 计算相对于迷宫的网格坐标
    maze_start_x, maze_start_y = blocks[0]['x'], blocks[0]['y']

    # 检查鼠标是否在迷宫区域内
    if (mouse_x >= maze_start_x and mouse_x <= maze_start_x + 20 * BLOCK_SIZE and
            mouse_y >= maze_start_y and mouse_y <= maze_start_y + 15 * BLOCK_SIZE):
        grid_x = (mouse_x - maze_start_x) // BLOCK_SIZE
        grid_y = (mouse_y - maze_start_y) // BLOCK_SIZE

        # 创建半透明背景
        coord_surf = pygame.Surface(COORD_BOX_SIZE, pygame.SRCALPHA)
        coord_surf.fill((0, 0, 0, 180))  # 黑色半透明(alpha=180)

        # 绘制坐标文本
        coord_text = pixel_font.render(f"({grid_x},{grid_y})", True, GOLD)

        # 计算显示位置，避免超出屏幕
        display_x = mouse_x + 10 if mouse_x + 10 + COORD_BOX_SIZE[0] < SCREEN_WIDTH else mouse_x - COORD_BOX_SIZE[0] - 10
        display_y = mouse_y - 30 if mouse_y - 30 > 0 else mouse_y + 10

        screen.blit(coord_surf, (display_x, display_y))
        screen.blit(coord_text, (display_x + 5, display_y + 5))


def draw_minimap() -> None:
    """绘制小地图"""
    if not blocks or not players:  # 确保迷宫和玩家已创建
        return

    # 创建小地图表面
    minimap_surf = pygame.Surface((MINIMAP_SIZE, MINIMAP_SIZE))
    minimap_surf.fill(MINIMAP_BG)

    # 计算缩放比例
    scale_x = MINIMAP_SIZE / (20 * BLOCK_SIZE) * BLOCK_SIZE
    scale_y = MINIMAP_SIZE / (15 * BLOCK_SIZE) * BLOCK_SIZE

    # 绘制障碍物（不可通行的方块）
    for block in blocks:
        if not block['way']:
            pygame.draw.rect(
                minimap_surf,
                GRAY,
                (block['grid_x'] * scale_x, block['grid_y'] * scale_y,
                 scale_x, scale_y)
            )

    # 绘制起点和终点
    start_block = blocks[0]
    pygame.draw.rect(
        minimap_surf,
        RED,
        (start_block['grid_x'] * scale_x, start_block['grid_y'] * scale_y,
         scale_x, scale_y)
    )

    end_block = next(b for b in blocks if b.get('ending', False))
    pygame.draw.rect(
        minimap_surf,
        RED,
        (end_block['grid_x'] * scale_x, end_block['grid_y'] * scale_y,
         scale_x, scale_y)
    )

    # 绘制玩家位置
    for player in players:
        pygame.draw.circle(
            minimap_surf,
            YELLOW,
            (int(player['grid_x'] * scale_x + scale_x/2),
             int(player['grid_y'] * scale_y + scale_y/2)),
            int(min(scale_x, scale_y) / 2)
        )

    # 绘制小地图边框
    pygame.draw.rect(minimap_surf, MINIMAP_BORDER,
                     (0, 0, MINIMAP_SIZE, MINIMAP_SIZE), 2)

    # 绘制小地图标题
    title = pixel_font.render("地图", True, WHITE)
    minimap_surf.blit(title, (5, 5))

    # 将小地图绘制到屏幕上
    screen.blit(minimap_surf, MINIMAP_POS)


def draw_player_info() -> None:
    """绘制玩家信息（步数等）"""
    if not players:
        return

    # 创建信息面板背景
    panel_surf = pygame.Surface((90, 50), pygame.SRCALPHA)
    panel_surf.fill((0, 0, 0, 180))  # 黑色半透明

    # 绘制玩家信息
    for i, player in enumerate(players):
        info_text = pixel_font.render(
            f"{player['name']}:\n {player['step_counter']}",
            True, GOLD
        )
        panel_surf.blit(info_text, (5, 5 + i * 25))

    # 绘制到屏幕左上角
    screen.blit(panel_surf, (5, 5))


def render_game() -> None:
    """渲染游戏画面"""
    try:
        screen.fill(WHITE)

        # 绘制按钮
        for button in buttons:
            draw_button(button)

        # 绘制所有方块
        for block in blocks:
            draw_block(block)

        # 绘制所有玩家
        for player in players:
            draw_player(player)

        # 绘制界面信息
        draw_coordinates()       # 坐标显示
        draw_minimap()           # 小地图
        draw_player_info()       # 玩家信息

        # 更新显示
        pygame.display.flip()

    except Exception as e:
        print(f"渲染游戏画面时发生错误: {e}")


def start_game() -> None:
    """游戏主循环"""
    global blocks, players, start_game_flag
    try:
        running = True
        fps_counter = 0
        frame_timer = pygame.time.get_ticks()

        while running:
            clock.tick(60)  # 控制帧率为 60 FPS

            # 更新FPS计数器
            current_time = pygame.time.get_ticks()
            fps_counter += 1
            if current_time - frame_timer > 1000:  # 每秒更新一次
                pygame.display.set_caption(f"Pygame 自动寻路 | FPS: {fps_counter}")
                fps_counter = 0
                frame_timer = current_time

            # 事件处理
            running = handle_events()

            # 更新游戏状态
            if start_game_flag == "start":
                if not update_game_state():
                    running = False
            if start_game_flag == "reset":
                blocks = []
                players = []
                no_way_blocks.clear()
                # 创建迷宫和玩家
                create_migong()
                create_player()
                start_game_flag = "None"

            # 渲染游戏画面
            render_game()

    except Exception as e:
        print(f"游戏运行时发生错误: {e}")
    finally:
        pygame.quit()
        sys.exit()


def main() -> None:
    """程序入口点"""
    try:
        # 加载图片资源
        load_image()
        # 创建迷宫和玩家
        create_migong()
        create_player()
        # 创建按钮
        create_button()

        # 启动游戏
        start_game()

    except Exception as e:
        print(f"程序主函数执行时发生错误: {e}")
    finally:
        pygame.quit()
        sys.exit()


if __name__ == '__main__':
    main()
